/**
 *  记录常用的一些函数.
 *
 */
let myTools = (function () {

    /**========================================================
     * Types:
     * 包含的 apis:
     * isArray
     * isFunction
     * isObject
     * isArguments
     * isString
     * isNumber
     * isDate
     * isRegExp
     * isError
     * isSymbol
     * isMap
     * isWeakMap
     * isSet
     * isWeakSet
     *
     */
    const Types = {
        isArray(obj) {
            return Array.isArray(obj);
        },

        isFunction(obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },

        isObject(obj) {
            let type = typeof obj;
            return type === 'function' || type === 'object' && !!obj;
        },
    };

    ['Arguments', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'].forEach(name => {
        Types['is' + name] = function (obj) {
            return Object.prototype.toString.call(obj) === '[object ' + name + ']';
        };
    });

    // 深度clone
    function deepClone(obj){
        let newObject;
        if(obj === null) {
            return null;
        }
        else if (!(obj instanceof Object)) {
            return obj;
        }
        else if (obj instanceof Date) {
            return new Date(obj);
        }
        else if (obj instanceof Function) {
            return Object.assign(obj);
        }
        else if (obj instanceof RegExp) {
            return new RegExp(obj);
        }
        else if (obj instanceof Array){
            newObject = [];
            for(let item of obj) {
                newObject.push(deepClone(item));
            }
        }
        else {
            newObject = Object.assign(Object.create(Object.getPrototypeOf(obj)),obj);
            for(let key of Object.keys(obj)) {

                if(newObject[key] !== obj)
                {
                    newObject[key] = deepClone(obj[key]);
                }else
                {
                    newObject[key] = newObject;
                }
            }
        }
        return newObject;
    }


    /**
     */
    function random(min, max) {
        if (max == null) {
            max = min;
            min = 0;
        }
        return min + Math.floor(Math.random() * (max - min + 1));
    }

    function clamp(x,minVal,maxVal) {
        return  Math.min(Math.max(x, minVal), maxVal);
    }

    function smoothstep (edge0,edge1,x) {
        let t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
        return t * t * (3.0 - 2.0 * t);
    }

    // For element i of the return value, 0.0 is returned if x[i] < edge[i], and 1.0 is returned otherwise.
    function step(edge,x) {
        if(x < edge){
            return 0;
        }else{
            return 1;
        }
    }

    // 会改变 arr
    function shuffle(arr) {
        let len = arr.length;
        for (let i = len - 1; i > 0; i--) {
            let j = Math.floor(Math.random() * (i + 1));
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
        return arr;
    }

    /**
     * [1,2,3,4,5,6,7] => [[1,2,3],[4,5,6],[7]]
     * @param arr
     * @param size
     */
    function sliceArrToMat2(arr,size) {
        let ret = [];
        let len = arr.length;
        if(len < 1){
            return ret;
        }
        for(let i = 0; i < len;i += size){
            ret.push(arr.slice(i,i+size));
        }
        return ret;
    }

    /**
     * 将 x 在 [a,b]区域, 映射到 在 [c,d] 的 x1
     * @param x
     * @param a
     * @param b
     * @param c
     * @param d
     * @return x1
     */
    function mapValue(x,a,b,c,d) {
        return (x - a) * (d - c) / (b - a)  + c;
    }

    function mix(min,max,x) {
        return min * (1-x) +max * x;
    }


    /**
     * 随机种子.
     * 可以生成确定性的随机数
     *  let func1 = seed(1234);
     *  let value = func1(); //  value 的值是确定性的.
     *
     * @param s
     * @return {function(): number}
     */
    function seed(s) {    // eslint-disable-line
        let m_w  = s;
        let m_z  = 987654321;
        let mask = 0xffffffff;

        return function() {
            m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
            m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;

            let result = ((m_z << 16) + m_w) & mask;
            result /= 4294967296;

            return result + 0.5;
        };
    }

    function asset(value,msg) {
        if(!value){
            throw new Error(`error msg:${msg}`);
        }
    }

    /**
     * 将数组arr 拆分为 每个大小为size的数组 的数组
     * 数组的最后一个数组 的大小不一定为 size
     * @param arr
     * @param size
     * @return {Array}
     */
    function arraySplit(arr,size){
        let ret = [];
        let len = arr.length;
        if(len < 1){
            return ret;
        }
        for(let i = 0; i < len;i += size){
            ret.push(arr.slice(i,i+size));
        }
        return ret;
    }

    /**
     * 将 2019-11-25 00:00:00 这样的格式转换成 Date 对象
     * @param str
     * @return {Date}
     */
    function strTimeToDate(str){
        let arr = str.split(' ');
        if(arr.length < 1){
            throw new Error(`解析 str:${str} error!`);
        }

        if(arr.length > 2){
            arr.length = 2;
        }
        let daystr = arr[0];

        let dayArr = daystr.split('-');
        let year = Number(dayArr[0]);
        let month = Number(dayArr[1]) - 1;
        let day = Number(dayArr[2]);

        let hour,minitue,sec;
        if(arr.length === 1){
            hour = minitue = sec = 0;
        }else{
            let timestr = arr[1];
            let timeArr = timestr.split(':');
            hour = Number(timeArr[0]);
            minitue = Number(timeArr[1]);
            sec = Number(timeArr[2]);
        }

        let d = new Date();
        d.setFullYear(year);
        d.setMonth(month);
        d.setDate(day);
        d.setHours(hour);
        d.setMinutes(minitue);
        d.setSeconds(sec);
        return d;
    }


    // 添加一个时间格式化方法
    /**
     *  为了 ios 和 android 都兼容
     *  使用方法：
     *  let d = new Date();
     *  let start = d.pattern('yyyy.MM.dd');
     */
    if(!Date.prototype.pattern){
        Date.prototype.pattern = function(fmt) {  // eslint-disable-line
            let o = {
                'M+' : this.getMonth()+1, //月份
                'd+' : this.getDate(), //日
                'h+' : this.getHours()%12 === 0 ? 12 : this.getHours()%12, //小时
                'H+' : this.getHours(), //小时
                'm+' : this.getMinutes(), //分
                's+' : this.getSeconds(), //秒
                'q+' : Math.floor((this.getMonth()+3)/3), //季度
                'S' : this.getMilliseconds() //毫秒
            };
            let week = {
                '0' : '/u65e5',
                '1' : '/u4e00',
                '2' : '/u4e8c',
                '3' : '/u4e09',
                '4' : '/u56db',
                '5' : '/u4e94',
                '6' : '/u516d'
            };
            if(/(y+)/.test(fmt)){
                fmt=fmt.replace(RegExp.$1, (this.getFullYear()+'').substr(4 - RegExp.$1.length));
            }
            if(/(E+)/.test(fmt)){
                fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? '/u661f/u671f' : '/u5468') : '')+week[this.getDay()+'']);
            }
            for(let k in o){
                if(new RegExp('('+ k +')').test(fmt)){
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00'+ o[k]).substr((''+ o[k]).length)));
                }
            }
            return fmt;
        };
    }

    let genId = function() {
        let __id = 0;
        return function () {
            return ++__id;
        }
    }();

    /**
     * 把 src 中的内容  混合到 dst 中
     * @param src
     * @param dst
     */
    function mixObj(src,dst){
        for(let key in src){
            if(!(key in dst)){
                dst[key] = deepClone(src[key]);
            }
        }
    }

    /**
     * client:浏览器下 字符串 转  base64 编/解 码
     * https://developer.mozilla.org/zh-CN/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer
     */
    function btoaUTF16 (sString) {
        let aUTF16CodeUnits = new Uint16Array(sString.length);
        Array.prototype.forEach.call(aUTF16CodeUnits, function (el, idx, arr) { arr[idx] = sString.charCodeAt(idx); });
        return btoa(String.fromCharCode.apply(null, new Uint8Array(aUTF16CodeUnits.buffer)));
    }

    function atobUTF16 (sBase64) {
        let sBinaryString = atob(sBase64), aBinaryView = new Uint8Array(sBinaryString.length);
        Array.prototype.forEach.call(aBinaryView, function (el, idx, arr) { arr[idx] = sBinaryString.charCodeAt(idx); });
        return String.fromCharCode.apply(null, new Uint16Array(aBinaryView.buffer));
    }

    return {
        version:'v1.0.1', //update 2020/08/28
        Types:Types,
        deepClone:deepClone,
        random:random,
        clamp:clamp,
        smoothstep:smoothstep,
        step:step,
        shuffle:shuffle,
        sliceArrToMat2:sliceArrToMat2,
        mapValue:mapValue,
        mix:mix,
        asset:asset,
        arraySplit:arraySplit,
        strTimeToDate:strTimeToDate,
        genId:genId,
        mixObj:mixObj,
    };

})();


window.myTools = myTools;
module.exports = myTools;
